"""
token password 验证

pip install python-jose

pip install passlib

"""
from datetime import datetime, timedelta

from distutils.util import strtobool
from fastapi import Depends, Request, HTTPException
from jose import JWTError, jwt
from passlib.context import CryptContext
from sqlalchemy.orm import Session
from fastapi.security import OAuth2PasswordBearer
from contextvars import ContextVar
from typing import Optional

from app.auth.models import AuthUsers
from setting import config
from common.database import get_db
from common.redispy import redispy
from core.response import resp_401, resp_403


class Security(object):

    def __init__(self, secret_key=config.SECRET_KEY, algorithm='HS256'):
        """
        :param secret_key：密鑰(str类型)
        :param algorithm： 加密算法(str类型)
        """
        self.secret_key = secret_key
        self.algorithm = algorithm
        self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

    def create_password_hash(self, password: str) -> str:
        return self.pwd_context.hash(password)

    def create_access_token(self, data: dict, expires_delta: int = config.ACCESS_TOKEN_EXPIRE_MINUTES) -> str:
        to_encode = data.copy()
        to_encode.update({"exp": datetime.utcnow() + timedelta(minutes=expires_delta)})
        encoded_jwt = jwt.encode(claims=to_encode, key=self.secret_key, algorithm=self.algorithm)
        # token信息存入redis
        redispy.set_token(data["username"], encoded_jwt)
        return encoded_jwt

    def verify_password(self, plain_password: str, hashed_password: str) -> bool:
        return self.pwd_context.verify(plain_password, hashed_password)

    def verify_access_token(self, token: str):
        try:
            payload = jwt.decode(token=token, key=self.secret_key, algorithms=self.algorithm)
            if not payload.get("username"):
                return False
        except JWTError:
            return False
        return payload


x_user_name: ContextVar[Optional[str]] = ContextVar('x_user_name', default="")

security = Security()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/ops/api/v1/login")


def token_checker(db: Session = Depends(get_db), token: str = Depends(oauth2_scheme)):
    payload = security.verify_access_token(token)
    if not payload:
        return resp_401()
    
    # 判断token是否在redis里面
    if not redispy.is_token_exists(payload["username"], token):
        return resp_401("token invalid")
        
    # 判断用户是否激活
    user = AuthUsers.get_by_uuid(db, payload.get("uuid"))
    if not user.get("is_active"):
        return resp_401("user is not active")
    
    payload.update({"is_superuser": user.get("is_superuser")})
    return payload


def permission_checker(request: Request, db: Session = Depends(get_db), user: dict = Depends(token_checker)):
    if user.get('is_superuser'):
        return user
    allowed_permission = {"method": request.method.lower(), "url": request.url.path}
    # if allowed_permission in WHITE_LIST:
    #     return user
    user_permissions = AuthUsers.get_permissions_by_uuid(db, user.get("uuid"))
    if allowed_permission not in user_permissions:
        return resp_403()
    return user
